package com.yeskery.nut.util;

import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * 算法工具类
 * @author sunjay
 * 2024/8/30
 */
public class DigestUtils {

    /** MD5 */
    private static final String MD5_ALGORITHM_NAME = "MD5";

    /** SHA */
    private static final String SHA_ALGORITHM_NAME = "SHA";

    /** 哈希字符数组 */
    private static final char[] HEX_CHARS = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};


    /**
     * 私有化构造方法
     */
    private DigestUtils() {
    }

    /**
     * md5函数
     * @param bytes 字节数组
     * @return md5哈希值
     */
    public static byte[] md5Digest(byte[] bytes) {
        MessageDigest digest = getDigest(MD5_ALGORITHM_NAME);
        digest.update(bytes);
        return digest.digest();
    }

    /**
     * md5函数
     * @param inputStream 输入流
     * @return md5哈希值
     * @throws IOException IOException
     */
    public static byte[] md5Digest(InputStream inputStream) throws IOException {
        return getAlgorithmDigestUpdateBytes(getDigest(MD5_ALGORITHM_NAME), inputStream);
    }

    /**
     * md5函数
     * @param bytes 字节数组
     * @return md5哈希值
     */
    public static String md5DigestAsHex(byte[] bytes) {
        return new String(encodeHex(md5Digest(bytes)));
    }

    /**
     * md5函数
     * @param inputStream 输入流
     * @return md5哈希值
     * @throws IOException IOException
     */
    public static String md5DigestAsHex(InputStream inputStream) throws IOException {
        return new String(encodeHex(md5Digest(inputStream)));
    }

    /**
     * md5函数
     * @param str 字符串
     * @return md5哈希值
     */
    public static String md5DigestAsHex(String str) {
        return new String(encodeHex(md5Digest(str.getBytes())));
    }

    /**
     * sha函数
     * @param bytes 字节数组
     * @return md5哈希值
     */
    public static byte[] shaDigest(byte[] bytes) {
        MessageDigest digest = getDigest(SHA_ALGORITHM_NAME);
        digest.update(bytes);
        return digest.digest();
    }

    /**
     * sha函数
     * @param inputStream 输入流
     * @return md5哈希值
     * @throws IOException IOException
     */
    public static byte[] shaDigest(InputStream inputStream) throws IOException {
        return getAlgorithmDigestUpdateBytes(getDigest(SHA_ALGORITHM_NAME), inputStream);
    }

    /**
     * sha函数
     * @param bytes 字节数组
     * @return md5哈希值
     */
    public static String shaDigestAsHex(byte[] bytes) {
        return new String(encodeHex(md5Digest(bytes)));
    }

    /**
     * sha函数
     * @param inputStream 输入流
     * @return md5哈希值
     * @throws IOException IOException
     */
    public static String shaDigestAsHex(InputStream inputStream) throws IOException {
        return new String(encodeHex(md5Digest(inputStream)));
    }

    /**
     * sha函数
     * @param str 字符串
     * @return md5哈希值
     */
    public static String shaDigestAsHex(String str) {
        return new String(encodeHex(md5Digest(str.getBytes())));
    }

    /**
     * 获取算法对象
     * @param algorithm 算法名称
     * @return 算法对象
     */
    private static MessageDigest getDigest(String algorithm) {
        try {
            return MessageDigest.getInstance(algorithm);
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalStateException("Could not find MessageDigest with algorithm \"" + algorithm + "\"", e);
        }
    }

    /**
     * 获取指定算法更新后的哈希值
     * @param digest 算法
     * @param inputStream 输入流
     * @return 哈希值
     * @throws IOException IOException
     */
    private static byte[] getAlgorithmDigestUpdateBytes(MessageDigest digest, InputStream inputStream) throws IOException {
        byte[] buffer = new byte[4096];
        int bytesRead;
        while((bytesRead = inputStream.read(buffer)) != -1) {
            digest.update(buffer, 0, bytesRead);
        }
        return digest.digest();
    }

    /**
     * 哈希字符转换
     * @param bytes 字节数组
     * @return 哈希字符数组
     */
    private static char[] encodeHex(byte[] bytes) {
        char[] chars = new char[32];

        for(int i = 0; i < chars.length; i += 2) {
            byte b = bytes[i / 2];
            chars[i] = HEX_CHARS[b >>> 4 & 15];
            chars[i + 1] = HEX_CHARS[b & 15];
        }

        return chars;
    }
}
